const { exec, spawn } = require('child_process');
const { createLogger } = require('./logger');

const logger = createLogger('shellExecutor');

/**
 * Execute a shell command and return the result
 * @param {string} command - Command to execute
 * @param {object} options - Options for exec
 * @returns {Promise<{stdout: string, stderr: string}>}
 */
function executeCommand(command, options = {}) {
  return new Promise((resolve, reject) => {
    logger.debug(`Executing command: ${command}`);
    
    exec(command, options, (error, stdout, stderr) => {
      if (error) {
        logger.error(`Command execution error: ${error.message}`);
        logger.error(`stderr: ${stderr}`);
        reject(error);
        return;
      }
      
      if (stderr) {
        logger.debug(`Command stderr: ${stderr}`);
      }
      
      logger.debug(`Command stdout: ${stdout}`);
      resolve({ stdout, stderr });
    });
  });
}

/**
 * Execute a shell command with real-time output streaming
 * @param {string} command - Command to execute
 * @param {Array<string>} args - Command arguments
 * @param {object} options - Options for spawn
 * @param {function} onData - Callback for stdout data
 * @param {function} onError - Callback for stderr data
 * @returns {Promise<number>} - Exit code
 */
function executeCommandWithStream(command, args = [], options = {}, onData = null, onError = null) {
  return new Promise((resolve, reject) => {
    logger.debug(`Executing command with stream: ${command} ${args.join(' ')}`);
    
    const childProcess = spawn(command, args, {
      ...options,
      shell: true
    });
    
    childProcess.stdout.on('data', (data) => {
      const output = data.toString();
      logger.debug(`Command stdout: ${output}`);
      if (onData) onData(output);
    });
    
    childProcess.stderr.on('data', (data) => {
      const output = data.toString();
      logger.debug(`Command stderr: ${output}`);
      if (onError) onError(output);
    });
    
    childProcess.on('close', (code) => {
      logger.debug(`Command exited with code ${code}`);
      if (code === 0) {
        resolve(code);
      } else {
        reject(new Error(`Command exited with code ${code}`));
      }
    });
    
    childProcess.on('error', (error) => {
      logger.error(`Command execution error: ${error.message}`);
      reject(error);
    });
  });
}

module.exports = {
  executeCommand,
  executeCommandWithStream
}; 